package com.yy.micro.magic.core.processor.impl;

import com.yy.micro.magic.core.annotation.Processor;
import com.yy.micro.magic.core.context.MagicCubeContext;
import com.yy.micro.magic.core.domain.MagicRequest;
import com.yy.micro.magic.core.domain.MagicResponse;
import com.yy.micro.magic.core.filter.FilterChain;
import com.yy.micro.magic.core.model.PlanSchedule;
import com.yy.micro.magic.core.model.ResultCollector;
import com.yy.micro.magic.core.model.RuleDescriptor;
import com.yy.micro.magic.core.model.RuntimeMetadata;
import com.yy.micro.magic.core.result.ResultStrategy;
import com.yy.micro.magic.core.result.runner.ResultRunner;
import com.yy.micro.magic.core.result.value.ValueStrategy;

import org.springframework.core.annotation.Order;

import java.util.List;
import java.util.Map;
 /**
   * @author YY
   * @version 1.0.0
   * @update 2021/9/22 9:39 下午
   * @description: 基于策略模式的变换处理器。
 */
@Processor("strategy")
@Order(5)
public class StrategyMagicProcessor extends MagicProcessorAdapter {

    @Override
    public MagicResponse process(final MagicRequest request, final MagicCubeContext context) {
        final ResultCollector collector = new ResultCollector(context);
        // find plan schedule and set to request
        findPlanSchedule(request, context);
        //do foreach all runtime data,sift out some data that accord with condition.
        doFilterProcess(request, collector);
        //do run schedule rule
        doScheduleProcess(request, collector);
        List<ResultRunner> runners = context.getComponentManager().findResultRunners(request.getPlanCode());
        if(null != runners && !runners.isEmpty()){
            runners.stream().forEach(r-> r.run(collector));
        }
        doPostProcess(request,collector);
//        handleRunner(request, context, collector);
        return collector.response();
    }

    private void doPostProcess(final MagicRequest request, final ResultCollector collector) {
        List<ValueStrategy> strategies =collector.getContext().getComponentManager().getValueStrategies(request.getValueStrategies());
        ResultStrategy<Map<String,Object>> strategy = ResultStrategy.NameValueResultStrategy.create(strategies, request.getNameStrategy());
        collector.setResultStrategy(strategy);
    }


    private void doScheduleProcess(final MagicRequest request, final ResultCollector collector) {
        request.getSchedule().findConventionRules().forEach(r-> execute(r.getKey(), r, null, request, collector, true));// 遍历映射模板节点
    }

    private void doFilterProcess(final MagicRequest request, final ResultCollector collector) {
        request.getRuntimeData().forEach((k, v) -> execute(k, RuleDescriptor.create(k), v, request, collector, request.isIncludeSourceData()));// 遍历请求入参节点
    }


    /**
     * 根据请求中指定的资源id和上下文找到对应的映射配置
     *
     * @param request
     * @param context
     * @return
     */
    protected PlanSchedule findPlanSchedule(final MagicRequest request, final MagicCubeContext context) {
        final String planCode = request.getPlanCode();// getAdditionalData().get("processType").toString();
        PlanSchedule schedule = context.findPlanSchedule(planCode);
        if (null == schedule){
            logger.warn("Not find schedule[" + planCode + "]");
            throw new RuntimeException("\"Not find schedule[\" + planCode + \"]");
        }
        request.setSchedule(schedule);
        request.setContext(context);
        return schedule;
    }

    /**
     * @param key
     *            节点路径
     * @param rule
     *            模板中映射配置
     * @param value
     *            请求中的节点值
     * @param request
     *            Map请求
     * @param collector
     *            结果收集器
     */
    private void execute(final String key, final RuleDescriptor rule , final Object value, final MagicRequest request,
                         final ResultCollector collector, final boolean includeSourceData) {
        final RuntimeMetadata metadata = RuntimeMetadata.create(key).from(request).setCollector(collector);
        metadata.setValue(value).setRule(rule).setIncludeRuntime(includeSourceData);// 请求节点的value
        execute(metadata);
    }

    private  void execute(RuntimeMetadata metadata){
        FilterChain chain = metadata.getHeadChain();
        chain.doFilter(metadata,metadata.getCollector());
    }

}
